package icasue.reflect.handles.classes;

import icasue.reflect.handles.OAble;
import lombok.SneakyThrows;

import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2020/11/30 下午4:11
 * @UpdateDate:
 * @Description:
 */
public class ClassO implements OAble {

    static {
        try {
            Class<?> loadHandleOperate = ClassO.class.getClassLoader().loadClass("icasue.reflect.handles.HandleSupplier");
            Method reFindLookUpMethod = loadHandleOperate.getDeclaredMethod("reFindLookUp", new Class[]{Class.class});
            reFindLookUpMethod.setAccessible(true);
            ClassO.lookup = (MethodHandles.Lookup)reFindLookUpMethod.invoke(null, ClassO.class);
        }catch (Throwable e){
            throw new RuntimeException("`ClassO init error.`",e);
        }
    }

    static {
        findExtendableMetadataHandle();
        findRootMetadataHandle();
        findRelativeMetadataHandle();
        findIdentityMetadataHandle();
        findDeepMetadataHandle();
    }

    public static MethodHandles.Lookup lookup;

    //根可读源信息
    public static MethodHandle getName;
    public static MethodHandle getSimpleName;
    public static MethodHandle getCanonicalName;
    public static MethodHandle getModifiers;

    //相对原信息
    public static MethodHandle getClassLoader;
    public static MethodHandle getConstructor;
    public static MethodHandle getDeclaredConstructor;
    public static MethodHandle getConstructors;
    public static MethodHandle getDeclaredConstructors;
    public static MethodHandle getInterfaces;
    public static MethodHandle getSuperclass;
    public static MethodHandle getEnclosingClass;
    public static MethodHandle getPackage;


    //身份源信息
    public static MethodHandle isAssignableFrom;
    public static MethodHandle isInterface;
    public static MethodHandle isMemberClass;
    public static MethodHandle isAnnotation;
    public static MethodHandle isEnum;
    public static MethodHandle isArray;
    public static MethodHandle isInstance;
    public static MethodHandle isAnonymousClass;
    public static MethodHandle isPrimitive;

    //不定源信息(可扩展)
    public static MethodHandle getDeclaredMethod;
    public static MethodHandle getDeclaredMethods;
    public static MethodHandle getMethod;
    public static MethodHandle getMethods;
    public static MethodHandle getField;
    public static MethodHandle getDeclaredField;
    public static MethodHandle getFields;
    public static MethodHandle getDeclaredFields;
    public static MethodHandle getAnnotations;
    public static MethodHandle getAnnotationsByType;
    public static MethodHandle getDeclaredAnnotationsByType;
    public static MethodHandle getEnumConstants;


    //深度源信息
    public static MethodHandle newInstance;
    public static MethodHandle cast;


    @SneakyThrows(Throwable.class)
    private static void findExtendableMetadataHandle(){
        getDeclaredMethods = lookup.findVirtual(Class.class,"getDeclaredMethods",MethodType.methodType(Method[].class));
        getDeclaredMethod = lookup.findVirtual(Class.class, "getDeclaredMethod", MethodType.methodType(Method.class, String.class, Class[].class));
        getMethods = lookup.findVirtual(Class.class,"getMethods",MethodType.methodType(Method[].class));
        getMethod = lookup.findVirtual(Class.class,"getMethod",MethodType.methodType(Method.class, String.class, Class[].class));
        getDeclaredFields = lookup.findVirtual(Class.class,"getDeclaredFields",MethodType.methodType(Field[].class));
        getDeclaredField = lookup.findVirtual(Class.class,"getDeclaredField",MethodType.methodType(Field.class,String.class));
        getFields = lookup.findVirtual(Class.class,"getFields",MethodType.methodType(Field[].class));
        getField = lookup.findVirtual(Class.class,"getField",MethodType.methodType(Field.class,String.class));
        getDeclaredAnnotationsByType = lookup.findVirtual(Class.class,"getDeclaredAnnotationsByType",MethodType.methodType(Annotation[].class,Class.class));
        getAnnotations = lookup.findVirtual(Class.class,"getAnnotations",MethodType.methodType(Annotation[].class));
        getAnnotationsByType = lookup.findVirtual(Class.class,"getAnnotationsByType",MethodType.methodType(Annotation[].class,Class.class));
        getEnumConstants = lookup.findVirtual(Class.class,"getEnumConstants",MethodType.methodType(Object[].class));
    }

    @SneakyThrows(Throwable.class)
    private static void findRootMetadataHandle(){
        getName = lookup.findVirtual(Class.class,"getName",MethodType.methodType(String.class));
        getSimpleName = lookup.findVirtual(Class.class,"getSimpleName",MethodType.methodType(String.class));
        getCanonicalName = lookup.findVirtual(Class.class,"getCanonicalName",MethodType.methodType(String.class));
        getModifiers = lookup.findVirtual(Class.class,"getModifiers",MethodType.methodType(int.class));
    }

    @SneakyThrows(Throwable.class)
    private static void findRelativeMetadataHandle(){
        getClassLoader = lookup.findVirtual(Class.class,"getClassLoader",MethodType.methodType(ClassLoader.class));
        getConstructor = lookup.findVirtual(Class.class,"getConstructor",MethodType.methodType(Constructor.class,Class[].class));
        getDeclaredConstructor = lookup.findVirtual(Class.class,"getDeclaredConstructor",MethodType.methodType(Constructor.class,Class[].class));
        getConstructors = lookup.findVirtual(Class.class,"getConstructors",MethodType.methodType(Constructor[].class));
        getDeclaredConstructors = lookup.findVirtual(Class.class,"getDeclaredConstructors",MethodType.methodType(Constructor[].class));
        getInterfaces = lookup.findVirtual(Class.class,"getInterfaces",MethodType.methodType(Class[].class));
        getSuperclass = lookup.findVirtual(Class.class,"getSuperclass",MethodType.methodType(Class.class));
        getEnclosingClass = lookup.findVirtual(Class.class,"getEnclosingClass",MethodType.methodType(Class.class));
        getPackage = lookup.findVirtual(Class.class,"getPackage",MethodType.methodType(Package.class));
    }

    @SneakyThrows(Throwable.class)
    private static void findIdentityMetadataHandle(){
        isAssignableFrom = lookup.findVirtual(Class.class,"isAssignableFrom",MethodType.methodType(boolean.class,Class.class));
        isInterface = lookup.findVirtual(Class.class,"isInterface",MethodType.methodType(boolean.class));
        isMemberClass = lookup.findVirtual(Class.class,"isMemberClass",MethodType.methodType(boolean.class));
        isAnnotation = lookup.findVirtual(Class.class,"isAnnotation",MethodType.methodType(boolean.class));
        isEnum = lookup.findVirtual(Class.class,"isEnum",MethodType.methodType(boolean.class));
        isArray = lookup.findVirtual(Class.class,"isArray",MethodType.methodType(boolean.class));
        isInstance = lookup.findVirtual(Class.class,"isInstance",MethodType.methodType(boolean.class,Object.class));
        //inner class.
        isAnonymousClass = lookup.findVirtual(Class.class,"isAnonymousClass",MethodType.methodType(boolean.class));
        //base type's box Type and void.
        isPrimitive = lookup.findVirtual(Class.class,"isPrimitive",MethodType.methodType(boolean.class));
    }

    @SneakyThrows(Throwable.class)
    private static void findDeepMetadataHandle(){
        newInstance = lookup.findVirtual(Class.class,"newInstance",MethodType.methodType(Object.class));
        cast = lookup.findVirtual(Class.class,"cast",MethodType.methodType(Object.class,Object.class));
    }
}
